import React, {Component, HTMLAttributes} from "react";
import raf from "raf";
import {SubscriptHandler} from "./Sticky";

export interface IStickyContainerProps extends HTMLAttributes<HTMLDivElement> {
  scrollDom?: () => HTMLElement | Window | null | undefined;
}

export default class StickyContainer extends Component<IStickyContainerProps, any> {
  private framePending: any;
  private rafHandle: any;
  private node?: any;
  private events = [
    "resize",
    "scroll",
    "touchstart",
    "touchmove",
    "touchend",
    "pageshow",
    "load"
  ];

  private subscribers: SubscriptHandler[] = [];

  static childContextTypes = {
    subscribe: (props: any) => {
    },
    unsubscribe: (props: any) => {
    },
    getParent: (props: any) => {
    }
  }

  constructor(props: any) {
    super(props);
  }

  getChildContext() {
    return {
      subscribe: this.subscribe,
      unsubscribe: this.unsubscribe,
      getParent: this.getParent
    }
  }

  subscribe = (handler: any) => {
    this.subscribers = this.subscribers.concat(handler);
  }

  unsubscribe = (handler: any) => {
    this.subscribers = this.subscribers.filter(current => current !== handler);
  }

  notifySubscribers = (evt: any) => {
    if (!this.framePending && this.node) {
      const {currentTarget} = evt;

      this.rafHandle = raf(() => {
        this.framePending = false;
        const {top, bottom} = this.node.getBoundingClientRect();
        this.subscribers.forEach(handler =>
          handler({
            distanceFromTop: top,
            distanceFromBottom: bottom,
            eventSource: currentTarget === window ? document.body : this.node
          })
        )
      })
      this.framePending = true;
    }
  }

  getParent = () => this.node;

  componentDidMount() {
    const {scrollDom} = this.props;
    // 适应非window滚动 zhanghj 20200318
    const dom = (scrollDom && scrollDom()) || window;
    this.events.forEach(event =>
      dom.addEventListener(event, this.notifySubscribers, {
        passive: true
      })
    )
  }

  componentWillUnmount() {
    if (this.rafHandle) {
      raf.cancel(this.rafHandle);
      this.rafHandle = null;
    }

    // 适应非window滚动 zhanghj 20200318
    const {scrollDom} = this.props;
    const dom = (scrollDom && scrollDom()) || window;
    this.events.forEach(event =>
      dom.removeEventListener(event, this.notifySubscribers)
    );
  }

  render() {
    const subProps = {...this.props};
    delete subProps.scrollDom;
    return (
      <div
        {...subProps}
        ref={node => (this.node = node)}
        onScroll={this.notifySubscribers}
        onTouchStart={this.notifySubscribers}
        onTouchMove={this.notifySubscribers}
        onTouchEnd={this.notifySubscribers}
      />
    )
  }
}
